home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / LIBRARY / SHDK_2 / SHCMDLIN.PAS < prev    next >
Pascal/Delphi Source File  |  1992-04-30  |  12KB  |  351 lines

  1. {$I SHDEFINE.INC}
  2.  
  3. {$I SHUNITSW.INC}
  4.  
  5. {$D-,L-}
  6. {$A-}
  7. unit ShCmdLin;
  8. {
  9.                                 ShCmdLin
  10.  
  11.                       A Command Line Parsing Unit
  12.  
  13.                                    by
  14.  
  15.                               Bill Madison
  16.  
  17.                    W. G. Madison and Associates, Ltd.
  18.                           13819 Shavano Downs
  19.                             P.O. Box 780956
  20.                        San Antonio, TX 78278-0956
  21.                              (512)492-2777
  22.                              CIS 73240,342
  23.  
  24.                   Copyright 1991 Madison & Associates
  25.                           All Rights Reserved
  26.  
  27.         This file may  be used and distributed  only in accord-
  28.         ance with the provisions described on the title page of
  29.                   the accompanying documentation file
  30.                               SKYHAWK.DOC
  31. }
  32.  
  33. Interface
  34. {------------}
  35.  
  36. Uses
  37.   ShList,
  38.   ShUtilPk,
  39.   Dos;
  40.  
  41. type
  42. {$IFNDEF Gen87}
  43.   extended  = real;
  44. {$ENDIF}
  45.   ValueType = (VtStr, VtReal, VtInt);
  46.   SwRec     = record
  47.                 Name  : string;
  48.                 case SwVal  : ValueType of
  49.                   VtStr : (StrVal : string);
  50.                   VtReal: (RealVal: extended);
  51.                   VtInt : (IntVal : integer);
  52.                 end; {SwRec}
  53.   CharSet = Set of Char;
  54.  
  55. procedure ClInit;
  56. {Initializes the command line switch list}
  57.  
  58. procedure ClClose;
  59. {Closes and frees the space associated with the command line switch list}
  60.  
  61. function GetSwitch(var Y : SwRec) : boolean;
  62. {Retrieves the next switch record. Returns FALSE if no more.}
  63.  
  64. function PopSwitch(var Y : SwRec) : boolean;
  65. {Retrieves the next switch record and frees its heap space.
  66.  Returns FALSE if no more.}
  67.  
  68. function ReadSwCh : char;
  69. {Reads and returns the current switch lead-in character}
  70.  
  71. procedure SetSwCh(C : char);
  72. {Sets the switch lead-in character to C}
  73.  
  74. Procedure ClParse(StrPtr : Pointer; StrOnly : Boolean;
  75.               LeadIn, ValDelim : CharSet; var Err : Integer);
  76. {USAGE: Parsing is accomplished by invoking the procedure ClParse with
  77.   five parameters:
  78.  
  79.   StrPtr of type Pointer is used to point to the string to be parsed. If
  80.   StrPtr is NIL, the command tail will be parsed.
  81.  
  82.   StrOnly of type Boolean is used to determine if switch values of type
  83.   String are to be forced, regardless of the form of the value. StrOnly
  84.   = True forces String values.
  85.  
  86.   LeadIn of type CharSet is used to identify the set of characters used
  87.   to mark the beginning of a switch. It is suggested that LeadIn be set
  88.   to [ ReadSwCh ]. The weakest condition used should be that the
  89.   expression ( ReadSwCh in LeadIn ) be TRUE.
  90.  
  91.   ValDelim of type CharSet is used to specify the set of characters
  92.   which may be used to separate the switch name from the switch value.
  93.  
  94.   X of type ClType (i.e., a doubly linked list as defined in unit
  95.   ShList) is used to return the names and values (if any) of any
  96.   switches included in the string being parsed. The ClType must be
  97.   initialized by a call to ClInit prior to the call to ClParse.
  98.  
  99.   Err of type Integer is used to return error conditions.
  100.  
  101.   The procedure returns a doubly linked list (as defined in unit ShList)
  102.   of records, each record containing the name and value of one command
  103.   line switch.
  104.  
  105.   All switches (with the optional exception of the first) are preceeded
  106.   with the normal DOS switch lead-in character with which your DOS is
  107.   configured (normally '/', but in pseudo-UNIX environments probably
  108.   '-').
  109.  
  110.   Switches may take values of type Real, LongInt, or String. In each
  111.   case, the switch value is separated from the switch name by one of the
  112.   characters specified in the parameter ValDelim. Switches which do not
  113.   take on any explicit value will be returned as type String, with a
  114.   value length of zero.
  115.  
  116.   Switches whose VALUE is intended to be of type String, but with a FORM
  117.   qualifying as a numeric must be enclosed in either single or double
  118.   quotation marks. Otherwise, it will be returned as a Real or LongInt,
  119.   as determined by its specific syntax (unless StrOnly = True in the
  120.   call).
  121.  
  122.   Additionally, any blanks included in String values will be packed out
  123.   unless the value is included in quotation marks. Further, if single
  124.   quote marks are to be included as part of a string value, then double
  125.   quotes must be used to define the value; and vice versa.
  126.  
  127. ERROR RETURNS:
  128.   The error parameter returns one of three values:
  129.             0 --> No error encountered.
  130.             1 --> Unbalanced single quotes encountered.
  131.             2 --> Unbalanced double quotes encountered.
  132.             3 --> Insufficient heap space to store the switch list.
  133. }
  134.  
  135.  
  136. Implementation
  137. {------------}
  138.  
  139. var
  140.   IsFirst : boolean;
  141.   X       : dlList;
  142.  
  143. procedure ClInit;
  144. {Initializes the command line switch list}
  145.   begin
  146.     dlListInit(X, SizeOf(SwRec));
  147.     IsFirst := true;
  148.     end; {ClInit}
  149.  
  150. procedure ClClose;
  151. {Closes and frees the space associated with the command line switch list}
  152.   begin
  153.     dlFree(X);
  154.     end; {ClClose}
  155.  
  156. function GetSwitch(var Y : SwRec) : boolean;
  157. {Retrieves the next switch record. Returns FALSE if no more.}
  158.   var
  159.     B1  : boolean;
  160.   begin
  161.     if IsFirst then begin
  162.       B1 := dlGetFirst(X, Y);
  163.       GetSwitch := B1;
  164.       IsFirst := false;
  165.       end
  166.     else begin
  167.       B1 := dlGetNext(X, Y);
  168.       GetSwitch := B1;
  169.       end;
  170.     end; {GetSwitch}
  171.  
  172. function PopSwitch(var Y : SwRec) : boolean;
  173. {Retrieves the next switch record and frees its heap space.
  174.  Returns FALSE if no more.}
  175.   var
  176.     B1  : boolean;
  177.   begin
  178.     B1 := dlPop(X, Y);
  179.     PopSwitch := B1;
  180.     end; {PopSwitch}
  181.  
  182. function ReadSwCh : char;
  183. {Reads the current switch lead-in character}
  184.   var
  185.     X     : Registers;
  186.   begin {Read the current character}
  187.     X.AH := $37;
  188.     X.AL := 0;
  189.     Intr($21, X);
  190.     ReadSwCh := char(X.DL);
  191.     end;
  192.  
  193. procedure SetSwCh(C : char);
  194. {Sets the switch lead-in character to C}
  195.   var
  196.     X     : Registers;
  197.   begin {Set the current character}
  198.     X.AH := $37;
  199.     X.AL := 1;
  200.     char(X.DL) := C;
  201.     Intr($21, X);
  202.     end;
  203.  
  204. Procedure ClParse(StrPtr : Pointer; StrOnly : Boolean;
  205.               LeadIn, ValDelim : CharSet; var Err : Integer);
  206.   const
  207.     MQT   = ^C;   {Master quote mark}
  208.     MVD   = ^M;   {Master value delimiter}
  209.     MLI   = ^[;   {Master lead-in mark}
  210.   var
  211.     CmdLine    : ^String;
  212.     CLine      : String;
  213.     QuoteState : (Qoff, Quote1, Quote2);
  214.     ValueState : (Voff, Von);
  215.     T1         : Integer;
  216.   Procedure PackCommandLine( var Err : Integer );
  217.   {Packs out all blanks not enclosed between balanced single or double
  218.    quotes, and replaces all such quote marks with Master Quotes. Replaces
  219.    all lead-in characters with Master Lead-In characters. Replaces all
  220.    value delimiters with Master Value Delimiters.}
  221.     const
  222.       PM       : CharSet = ['+','-'];
  223.     var
  224.       T1       : Integer;
  225.     begin
  226.       CLine := '';
  227.       QuoteState := Qoff;
  228.       ValueState := Voff;
  229.       For T1 := 1 to Length(CmdLine^) do
  230.         Case QuoteState of
  231.           Qoff   : Case CmdLine^[T1] of
  232.                      ' '  : ;
  233.                      '''' : begin
  234.                               QuoteState := Quote1;
  235.                               CLine := CLine + MQT;
  236.                               end;
  237.                      '"'  : begin
  238.                               QuoteState := Quote2;
  239.                               CLine := CLine + MQT;
  240.                               end;
  241.                      else begin
  242.                             if (T1 > 1) and
  243.                                (CLine[Length(CLine)] = MVD) and
  244.                                (CmdLine^[T1] in PM) then begin
  245.                               CLine := CLine + CmdLine^[T1];
  246.                               end
  247.                             else
  248.                               if (CmdLine^[T1] in LeadIn) and
  249.                                  (ValueState = Von) then begin
  250.                                 CLine := CLine + MLI;
  251.                                 ValueState := Voff;
  252.                                 end
  253.                               else
  254.                                 if (CmdLine^[T1] in ValDelim) and
  255.                                    (ValueState = Voff) then begin
  256.                                   CLine := CLine + MVD;
  257.                                   ValueState := Von;
  258.                                   end
  259.                                 else begin
  260.                                   CLine := CLine + CmdLine^[T1];
  261.                                   end;
  262.                             end;
  263.                      end;
  264.           Quote1 : Case CmdLine^[T1] of
  265.                      '''' : begin
  266.                               QuoteState := Qoff;
  267.                               CLine := CLine + MQT;
  268.                               end;
  269.                      else   CLine := CLine + CmdLine^[T1];
  270.                      end;
  271.           Quote2 : Case CmdLine^[T1] of
  272.                      '"'  : begin
  273.                               QuoteState := Qoff;
  274.                               CLine := CLine + MQT;
  275.                               end;
  276.                      else   CLine := CLine + CmdLine^[T1];
  277.                      end;
  278.           end;
  279.       If (Length(CLine) > 0) and (CLine[1] <> MLI) then
  280.         CLine := MLI + CLine;
  281.       Err := ord(QuoteState);
  282.       end; {PackCommandLine}
  283.   function MakeSwitchRecord : boolean;
  284.     var
  285.       WorkSpace : String;
  286.       Err       : Integer;
  287.       T1        : Integer;
  288.       SwitchRec : SwRec;
  289.     begin
  290.       Delete(CLine, 1, 1); {Strip leading MLI}
  291.       WorkSpace := CLine;
  292.       If Pos(MLI, WorkSpace) <> 0 then begin
  293.         WorkSpace[0] := chr(Pos(MLI, WorkSpace) - 1);
  294.         Delete(CLine, 1, Pos(MLI, CLine)-1);
  295.         end
  296.       else
  297.         CLine := '';
  298.       With SwitchRec do begin
  299.         If Pos(MVD, WorkSpace) <> 0 then begin
  300.           Name := Copy(WorkSpace, 1, Pos(MVD, WorkSpace)-1);
  301.           Delete(WorkSpace, 1, Pos(MVD, WorkSpace));
  302.           end
  303.         else begin
  304.           Name := WorkSpace;
  305.           WorkSpace := '';
  306.           end;
  307.     {Name has been set. Now get type and value}
  308.         If not StrOnly then begin
  309.           If Length(WorkSpace) = 0 then begin
  310.             SwVal   := VtStr;
  311.             StrVal  := '';
  312.             MakeSwitchRecord := dlPut(X, SwitchRec);
  313.             exit
  314.             end;
  315.           Val(WorkSpace, IntVal, Err);
  316.           If Err = 0 then begin
  317.             SwVal := VtInt;
  318.             MakeSwitchRecord := dlPut(X, SwitchRec);
  319.             exit
  320.             end;
  321.           Val(WorkSpace, RealVal, Err);
  322.           If Err = 0 then begin
  323.             SwVal := VtReal;
  324.             MakeSwitchRecord := dlPut(X, SwitchRec);
  325.             exit
  326.             end;
  327.           end; {If not StrOnly}
  328.         SwVal   := VtStr;
  329.         StrVal  := WorkSpace;
  330.         DelAll(StrVal, MQT, StrVal);
  331.         MakeSwitchRecord := dlPut(X, SwitchRec);
  332.         end; {With SwitchRec}
  333.       end; {MakeSwitchRecord}
  334.   begin {ClParse}
  335.     If StrPtr = nil then
  336.       CmdLine := Ptr(PrefixSeg, $0080)
  337.     else
  338.       CmdLine := StrPtr;
  339.     PackCommandLine(Err);
  340.     If (Length(CLine) = 0) or (Err <> 0) then exit;
  341.     While Pos(MLI, CLine) <> 0 do begin
  342.       if MakeSwitchRecord then
  343.         Err := 0
  344.       else begin
  345.         Err := 3;
  346.         exit;
  347.         end;
  348.       end;
  349.     end; {ClParse}
  350.   end.
  351.